Visual BasicにはCallByNameという関数があるよ、とbiacさんから教えて頂きました。
言語仕様は原稿執筆のために一通り目を通していますが、Microsoft.VisualBasic名前空間の中身は全て見たわけではないので、気付かなかったものもあります。
これは、メソッド等を名前で呼び出すもので、簡易なリフレクションのようなもの……と捉えることができます。
面白そうだから簡単なテストプログラムを1本書いてみようと取り組んだところ、意外にも私好みの問題に突き当たりました。
それは、以下のようなことです。
見出された問題 §
- Visual Basic 6.0のプロパティの操作にはGet/Set/Letの3種類が存在する
- Visual Basic.NET以降はこれがGet/Setの2種類に集約されLetが消えた
- CallByName関数で指定可能な呼び出しスタイルはMethod, Get, Set, Letの4種類
- Methodはプロパティ用ではなくメソッド用なので除外すると、Letを含む3種類が存在する
- しかし、Visual Basic.NET以降で作成されたオブジェクトにはLetの対象はあり得ない
- このLetは何のために存在する? 何の意味もない値? 互換性のために残されたもの? それとも意味がある?
疑問に対する答 §
いろいろ調べた結果、CallByName.Letを使用している例(Understanding the Word Object Model from a .NET Developer's Perspective)が見つかりました。Wordのオブジェクトを呼び出す事例で、おそらくCOMオブジェクトです。
つまり、COMとの互換性ラッパを経由してCOMオブへジェクトを呼び出す場合にも、CallByName関数は使用されるということです。
そして、COMの世界のプロパティにはGet, Set, Letの3種類が存在します。これをストレートにサポートするためには、Letが必要とされるわけです。
結論は以下のようになります。
- CallByName.Letは意味のある値。けしてダミー値ではない
- COMオブジェクトを直接呼び出すプログラムを作成するプログラマは使う可能性がある
- COMオブジェクトを直接呼び出すプログラムを扱わないプログラマが使う可能性は(おそらく)無い
- Visual Basic以外のプログラマ(C#等)でも、Microsoft.VisualBasic.CallByNameメソッドを使うことでCOMオブジェクトを呼び出すプログラムを作成する可能性があり得、その場合はCallByName.Letを使うかもしれない (上記の事例はVBだけでなくC#のサンプルもこれを使って記述されている)
検証プログラム §
Visual Basic 2005, コンソール アプリケーション。COM版BlatJをregsvr32してからプロジェクトの参照に入れる必要あり。
Module Module1
Sub Main()
' CallType.Methodの使用例
Dim hello As String = "hello"
' 直接呼び出し
Console.WriteLine(hello.ToUpper())
' CallByName呼び出し
Console.WriteLine(CallByName(hello, "ToUpper", CallType.Method))
' CallType.Getの使用例
Dim sb As New System.Text.StringBuilder
' 直接呼び出し
sb.Append("ab")
Console.WriteLine(sb.Length)
' CallByName呼び出し
sb.Append("cd")
Console.WriteLine(CallByName(sb, "Length", CallType.Get))
' CallType.Setの使用例
' 直接呼び出し
sb.Length = 2
Console.WriteLine(sb.Length)
' CallByName呼び出し
Dim dummy1 As Object = CallByName(sb, "Length", _
CallType.Set, 1)
Console.WriteLine(sb.Length)
' CallType.Letの使用例
Dim comblat As New COMBLATLib.Blat() ' COM版BlatJを使用
' 直接呼び出し
comblat.Subject = "test1"
Console.WriteLine(comblat.Subject)
' CallByName呼び出し
Dim dummy2 As Object = CallByName(comblat, "Subject", _
CallType.Let, "test2")
Console.WriteLine(comblat.Subject)
End Sub
End Module
実行結果 §
HELLO
HELLO
2
4
2
1
test1
test2
念のための補足 §
上記のソースでは、文字列をLetで扱っています。.NET Frameworkでは文字列はオブジェクトなので参照型になります。それゆえに、LetではなくSetで扱う対象であるかのように思えますが、VB6/COMの文字列は基本データ型の一部なのでLetで扱う範疇に入ります。
という説明が妥当かどうか確認しようと、資料を調べようとして挫折しました (汗。